package com.powernode.config;


import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.powernode.constant.AuthConstants;
import com.powernode.constant.BusinessEnum;
import com.powernode.constant.HttpConstants;
import com.powernode.impl.UserDetailsServiceImpl;
import com.powernode.model.LoginResult;
import com.powernode.model.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.PrintWriter;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


/**
 * security安全框架配置
 */
@Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 设置security安全框架走自己的认证流程
     * @param auth 用于配置 Spring Security 的身份验证管理器
     * @throws Exception 抛出异常
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //形参：需要实现UserDetailsService接口的实现类
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨站请求伪造
        http.cors().disable();
        //关闭跨域请求
        http.csrf().disable();
        //关闭使用策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //配置登录信息
         http.formLogin()
                    .loginProcessingUrl(AuthConstants.LOGIN_URL)//设置登录URL
                .successHandler(authenticationSuccessHandler())   //设置登录成功处理器
                .failureHandler(authenticationFailureHandler());  //设置登录失败处理器

        //配置登出信息
        http.logout()
                .logoutUrl(AuthConstants.LOGOUT_URL)            //设置登出url
                .logoutSuccessHandler(logoutSuccessHandler());//设置登出成功处理器

        //要求所有请求都需要进行身份的认证
        http.authorizeHttpRequests().anyRequest().authenticated();

        super.configure(http);
    }

    /**
     * 登录成功处理器
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return (request, response, authentication) -> {
            //1.设置响应头信息
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);

            //2.使用UUID当做token 此时只有key(token)而value是认证的用户信息（需要在UserDetailsServiceImpl里处理，
            // 可以从认证框架的上下文authentication获取认证对象，对象类型就是UserDetails接口，需要实现类(自己定义
            // 了SecurityUser)）
            String token = UUID.randomUUID().toString();

            //3.从security框架中获取认证用户对象(SecurityUser)并转为json格式的字符串
            //getPrincipal() 返回当前经过身份验证的用户的主体。这个主体通常是一个包含用户信息的对象，常见的实现是 UserDetails
            String userJsonStr = JSONObject.toJSONString(authentication.getPrincipal());
            //将token作为key，认证用户对象的json格式的字符串当value存入redis
            stringRedisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX+token,
                    userJsonStr,AuthConstants.TOKEN_TIME, TimeUnit.MINUTES);

            //封装一个登录同意结果对象
            LoginResult loginResult = new LoginResult(token,AuthConstants.TOKEN_TIME);
            //创建一个响应结果对象
            Result<LoginResult> result = Result.success(loginResult);

            //返回结果
            PrintWriter writer = response.getWriter();
            writer.write(new ObjectMapper().writeValueAsString(result));
            writer.flush();
            writer.close();
        };
    }

    /**
     * 登录失败处理器
     * @return 响应体中有Result对象：处理身份验证失败的情况(根据异常不同设置不同错误信息)的响应
     */
    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request, response, exception) -> {
            //设置响应头信息
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);

            //创建统一响应结果
            Result<Object> result = new Result<>();
            result.setCode(BusinessEnum.OPERATION_FAIL.getCode());
            if (exception instanceof BadCredentialsException){
                result.setMsg("用户名或密码有误");
            } else if (exception instanceof UsernameNotFoundException){
                result.setMsg("用户不存在");
            } else if (exception instanceof AccountExpiredException){
                result.setMsg("账号异常，请联系管理员");
            } else if (exception instanceof AccountStatusException){
                result.setMsg("账号异常，请联系管理员");
            } else if (exception instanceof InternalAuthenticationServiceException){
                result.setMsg(exception.getMessage());
            }else {
                result.setMsg(BusinessEnum.OPERATION_FAIL.getDesc());
            }

            //返回响应
            PrintWriter writer = response.getWriter();
            writer.write(new ObjectMapper().writeValueAsString(result));
            writer.flush();
            writer.close();

        };
    }

    /**
     * 登出成功处理器
     */
    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return (request, response, authentication) -> {
            response.setContentType(HttpConstants.APPLICATION_JSON);
            response.setCharacterEncoding(HttpConstants.UTF_8);

            //从请求头获取token
            String authorization = request.getHeader(AuthConstants.AUTHORIZATION);
            String token = authorization.replaceFirst(AuthConstants.BEARER, "");

            //从redis删除
            stringRedisTemplate.delete(AuthConstants.LOGIN_TOKEN_PREFIX+token);

            //返回结果
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(new Result<>());
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();

        };
    }

    /**
     * 密码加密器
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


}
